import os
import subprocess
import time
import psutil
import shutil
from typing import List, Tuple
from config import Config
import logging
from colorama import Fore, Back, Style
import random


# 自定义日志格式化类，用于根据日志级别添加颜色
class ColoredFormatter(logging.Formatter):
    def __init__(self, fmt=None, datefmt=None):
        # 调用父类的初始化方法
        super().__init__(fmt, datefmt)

    def format(self, record):
        # 默认日志文字颜色为白色
        log_color = Fore.WHITE
        # 根据日志级别设置颜色
        if record.levelno == logging.DEBUG:  # 调试级别
            level_color = Fore.CYAN
        elif record.levelno == logging.INFO:  # 信息级别
            level_color = Fore.GREEN
        elif record.levelno == logging.WARNING:  # 警告级别
            level_color = Fore.YELLOW
        elif record.levelno == logging.ERROR:  # 错误级别
            level_color = Fore.RED
        elif record.levelno == logging.CRITICAL:  # 严重错误级别
            level_color = Fore.RED + Back.WHITE
        else:
            level_color = Fore.WHITE  # 其他级别默认为白色
        
        # 格式化日志级别部分，并添加颜色
        level_msg = f'[{record.levelname}]'
        colored_level_msg = level_color + level_msg + Style.RESET_ALL
        
        # 格式化日志消息部分，保持文字颜色为白色
        log_msg = super().format(record).replace(level_msg, colored_level_msg, 1)
        
        # 返回带有颜色级别和白色文字的日志消息
        return log_color + log_msg + Style.RESET_ALL

# 配置日志系统
formatter = ColoredFormatter(f'{Fore.WHITE}[%(levelname)s]{Style.RESET_ALL} {Fore.LIGHTBLUE_EX}%(message)s{Style.RESET_ALL}')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logging.basicConfig(level=logging.DEBUG, handlers=[handler])

# 配置日志系统
# 创建一个日志格式化器，其中包含颜色
formatter = ColoredFormatter(f'[%(levelname)s] %(message)s')
# 创建一个日志处理器，用于将日志输出到控制台
handler = logging.StreamHandler()
# 将自定义的格式化器设置到处理器上
handler.setFormatter(formatter)
# 配置日志记录器，设置记录级别和处理器
logging.basicConfig(level=logging.DEBUG, handlers=[handler])

class AlgorithmTester:
   def __init__(self, solution_file: str):
       """
       初始化测试器
       :param solution_file: 解答文件路径(.c或.cpp)
       :param test_cases: 测试样例数量
       """
       self.abs_main_dir = os.path.dirname(os.path.abspath(__file__))
       self.solution_file = os.path.join(self.abs_main_dir, Config.SOLVE_FILE)
       self.data_dir = os.path.join(os.path.join(self.abs_main_dir, "questions"), Config.SAMPLE_DATA)
       self.test_cases = int(len(os.listdir(self.data_dir)) / 2)
       self.executable = os.path.join(self.abs_main_dir, Config.SOLVE_FILE.split(".")[0])
       
       # 确保data目录存在
       if not os.path.exists(self.data_dir):
           os.makedirs(self.data_dir)
           
   def compile_solution(self) -> bool:
       """
       编译解答文件
       :return: 是否编译成功
       """
       try:
           if self.solution_file.endswith('.c'):
               subprocess.run(['gcc', "-Wno-return-type", self.solution_file, '-o', self.executable], check=True)
           elif self.solution_file.endswith('.cpp'):
               subprocess.run(['g++', "-Wno-return-type", self.solution_file, '-o', self.executable], check=True)
           logging.info("编译成功!")
           return True
       except subprocess.CalledProcessError:
           logging.error("编译失败!")
           return False
           
   
   def test_solution(self) -> List[Tuple[bool, float, float]]:
        """
        测试解答程序
        :return: 每个测试样例的结果列表 [(是否通过, 运行时间, 内存使用)]
        """
        results = []
        for i in range(self.test_cases):
            # 运行程序并计时
            start_time = time.time()
            process = subprocess.Popen([f"{os.path.join(self.abs_main_dir, self.executable)}"],
                                stdin=open(f"{self.data_dir}/{i+1}.in", 'r'),
                                stdout=subprocess.PIPE)
            
            # 获取内存使用
            process_info = psutil.Process(process.pid)
            max_memory = 0

            end_time = time.time()
            run_time = end_time - start_time

            memory = process_info.memory_info().rss / 1024 / 1024  # MB
            
            output, _ = process.communicate()
            
            # 检查输出是否正确
            with open(f"{self.data_dir}/{i+1}.out", 'r') as f:
                expected = f.read().strip().replace('\r\n', '\n').replace('\r', '\n')
            actual = output.decode().strip().replace('\r\n', '\n').replace('\r', '\n')

            # 比较去除多余空格后的输出
            expected = " ".join(expected.split())
            actual = " ".join(actual.split())
            passed = expected == actual
            
            results.append((passed, run_time, memory))
        
        return results
   def run_tests(self):
       """
       运行完整的测试流程
       """
       logging.info("运行测试...")
       # 先编译
       self.compile_solution()
       # 获取结果
       results = self.test_solution()

       # 统计结果
       passed = sum(1 for r in results if r[0])
       total_time = sum(r[1] for r in results[1:])
       avg_time = total_time / self.test_cases
       avg_memory = sum(r[2] for r in results) / self.test_cases
       passed_num = 0

       # 输出每个测试样例的详细信息
       logging.info("详细测试结果:")

       #　打印成功的
       logging.info("================= Accepted =================")
       for i, (passed, time, memory) in enumerate(results, 1):
           status = "通过" if passed else "失败"
           if status == "通过":
               logging.info(Fore.LIGHTGREEN_EX + f"#{i}: {status} (时间: {time*1000:.2f}ms, 内存: {memory:.2f}MB)" + Style.RESET_ALL)
               passed_num += 1
       logging.info("===============  Wrong Answer ===============")
       for i, (passed, time, memory) in enumerate(results, 1):
           status = "通过" if passed else "失败"
           if status == "失败":
               logging.error(Fore.LIGHTRED_EX + f"#{i}: {status} (时间: {time*1000:.2f}ms, 内存: {memory:.2f}MB)" + Style.RESET_ALL)

       logging.info("=============================================")

       
       logging.info(f"测试结果:")
       logging.info(f"通过率: {passed_num}/{self.test_cases} ({passed_num/self.test_cases*100:.2f}%)")
       logging.info(f"平均运行时间: {avg_time*1000:.2f}ms")
       logging.info(f"平均内存使用: {avg_memory:.2f}MB")

       logging.info("=============================================")
   
   def cleanup(self):
       """
       清理生成的文件
       """
       if os.path.exists(self.executable):
           os.remove(self.executable)
       if os.path.exists(self.data_dir):
           shutil.rmtree(self.data_dir)

# 使用示例
if __name__ == "__main__":
   # 创建测试器实例
   tester = AlgorithmTester("solve.cpp")
   
   try:
       # 运行测试
       tester.run_tests()
   finally:
       # 清理文件（可选）
       # tester.cleanup()
       pass